Android Context知多少

Android Context知多少

四月 03, 2019

什么是Context

Android与Java最大的区别在于Android又自己的四大组件生命周期和上下文环境。这里的上下文环境就是所谓的Context。Android中的组件并不能像Java中的类一样直接通过new关键字来创建对象,而是需要与Context上下文环境进行绑定才能正常工作的。

Context继承结构

可以看到ContextWrapper和ContextImpl都是Context的子类,实现了Context类的各种方法。但实际上从命名可以看出来,ContextImpl才是Context真正的实现类,而ContextWrapper只是Context的封装类,它里面的所有方法实际上都是通过调用ContextImpl里面对应的实现方法来实现的。换言之,ContextWrapper实际上是ContextImpl类对Context类的代理。这点在ContextWrapper的类注释中有说明。

ContextWrapper类代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
* Proxying implementation of Context that simply delegates all of its calls to
* another Context. Can be subclassed to modify behavior without changing
* the original Context.
*/
public class ContextWrapper extends Context {
Context mBase;

public ContextWrapper(Context base) {
mBase = base;
}

/**
* Set the base context for this ContextWrapper. All calls will then be
* delegated to the base context. Throws
* IllegalStateException if a base context has already been set.
*
* @param base The new base context for this wrapper.
*/
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}

/**
* @return the base context as set by the constructor or setBaseContext
*/
public Context getBaseContext() {
return mBase;
}

@Override
public AssetManager getAssets() {
return mBase.getAssets();
}

@Override
public Resources getResources() {
return mBase.getResources();
}

@Override
public PackageManager getPackageManager() {
return mBase.getPackageManager();
}

@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}

@Override
public Looper getMainLooper() {
return mBase.getMainLooper();
}

@Override
public Context getApplicationContext() {
return mBase.getApplicationContext();
}
......
}

从代码中可以看到,ContextWrapper中的所有方法实现都是调用了mBase中的同名方法。而这里的mBase实际上就是ContextImpl的实例。应用在初始化的时候,系统会调用Application的attachBaseContext(Context base)方法,对mBase进行初始化。也就是说,在该方法之前,我们是不能调用Context里面的任何方法的,否则会报空指针异常。

Application的生命周期顺序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyApp extends Application {

public MyApp() {
// 在attachBaseContext()之前执行,此处调用Context方法会报错
}

@Override
public void onCreate() {
super.onCreate();
//在attachBaseContext()之后执行,官方推荐在这里做应用初始化操作
}

@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
//在构造方法之后,onCreate()之前执行,进行mBase初始化
}
}

getApplication()和getApplicationContext()

1
2
3
4
5
@Override
public Context getApplicationContext() {
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
}

可以看到,getApplicationContext()实际上就是调用了getApplication(),也就是说这两个方法返回的是同一个对象,其实也好理解,因为Application本来就是Context的子类。他们唯一的区别是:getApplicationContext()的作用域比getApplication()要广。getApplication()只能在Activity、Service中使用,而getApplicationContext()任何一个Context实例都可以调用。

Context引起的内存泄漏

context引起的内存泄漏一般出现在单例模式中使用了Activity的Context,因为单例的生命周期与应用程序的生命周期一样,而传入Activity的Context会使Activity在使用完之后得不到回收造成泄漏。所以,单例模式中的Context对象一定用使用ApplicationContext。因为Application是全局唯一的,并且生命周期也是与应用的生命周期等长的。